﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections;
using System.Reflection;
using System.Windows.Data;
using System.Linq;

namespace Fayshine.VA.Control
{
    public partial class ComboBoxTree : UserControl
    {

        public ComboBoxTree()
        {
            // Required to initialize variables
            InitializeComponent();
            this.LayoutUpdated += new EventHandler(ComboBoxTree_LayoutUpdated);
            // this.tvList.LostFocus += new RoutedEventHandler(tvList_LostFocus);
        }

        #region Dependency Properties

        #region IsExpandAll

        /// <summary>
        /// Set the TreeView state expand or collpase. The defatult is True.
        /// </summary>
        public static readonly DependencyProperty IsExpandAllProperty =
            DependencyProperty.Register("IsExpandAll", typeof(bool), typeof(ComboBoxTree), new PropertyMetadata(true));

        /// <summary>
        /// Get or set TreeView state. Default value is true
        /// </summary>
        public bool IsExpandAll
        {
            get
            {
                return (bool)this.GetValue(IsExpandAllProperty);
            }
            set
            {
                this.SetValue(IsExpandAllProperty, value);
            }
        }

        #endregion

        #region ItemSource

        /// <summary>
        /// Set the ComboBoxTree data source
        /// </summary>
        public static readonly DependencyProperty ItemsSourceProperty =
                 DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ComboBoxTree),
                 new PropertyMetadata(new PropertyChangedCallback(ItemsSourcePropertyChangedCallBack)));

        /// <summary>   
        /// Get or set data source  
        /// </summary>   
        public IEnumerable ItemsSource
        {
            get
            {
                return (IEnumerable)this.GetValue(ItemsSourceProperty);
            }
            set
            {
                this.SetValue(ItemsSourceProperty, value);
            }
        }

        /// <summary>
        /// Callback Event for ItemsSource property changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void ItemsSourcePropertyChangedCallBack(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            if (sender != null)
            {
                ComboBoxTree comboBoxTree = sender as ComboBoxTree;

                if (comboBoxTree.IsRecursionEnabled == false)
                {
                    // 以列表的方式加载数据   
                    comboBoxTree.LoadTreeViewDataWithList();
                }
                else
                {
                    //以递归的方式加载数据   
                    comboBoxTree.LoadTreeViewDataWithRecursion();
                }

            }
        }


        #endregion

        #region SelectedValuePath

        public static readonly DependencyProperty SelectedValuePathProperty = DependencyProperty.Register("SelectedValuePath",
            typeof(string), typeof(ComboBoxTree), null);

        public string SelectedValuePath
        {
            get
            {
                return (string)this.GetValue(SelectedValuePathProperty);
            }
            set
            {
                this.SetValue(SelectedValuePathProperty, value);
            }
        }

        #endregion

        #region DisplayMemberPath

        public static readonly DependencyProperty DisplayMemberPathProperty = DependencyProperty.Register("DisplayMemberPath",
            typeof(string), typeof(ComboBoxTree), null);

        public string DisplayMemberPath
        {
            get
            {
                return (string)this.GetValue(DisplayMemberPathProperty);
            }
            set
            {
                this.SetValue(DisplayMemberPathProperty, value);
            }
        }

        #endregion

        #region SelectedItem

        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
            typeof(object), typeof(ComboBoxTree), null);

        public object SelectedItem
        {
            get
            {
                return this.GetValue(SelectedItemProperty);
            }
            set
            {
                this.SetValue(SelectedItemProperty, value);
            }
        }

        #endregion

        #region SelectedValue

        public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue",
            typeof(object), typeof(ComboBoxTree), null);

        public object SelectedValue
        {
            get
            {
                return this.GetValue(SelectedValueProperty);
            }
            set
            {
                this.SetValue(SelectedValueProperty, value);
            }
        }

        #endregion

        #region IsRecursionEnabled

        /// <summary>
        /// Set whether need to load data by recursion. It needs to set ChildName/ParentName Property
        /// </summary>
        public static readonly DependencyProperty IsRecursionEnabledProperty =
          DependencyProperty.Register("IsRecursionEnabled", typeof(bool), typeof(ComboBoxTree), new PropertyMetadata(new PropertyChangedCallback(IsRecursionEnabledCallBack)));

        /// <summary>   
        /// Set whether need to load data by recursion. It needs to set ChildName/ParentName Property 
        /// If there is no ChildMemberPath和ParentMemberPath, it will not load data by recursion  
        /// </summary>   
        public bool IsRecursionEnabled
        {
            get
            {
                return (bool)this.GetValue(IsRecursionEnabledProperty);
            }
            set
            {
                //if (value == true)   
                //{   
                //    //---如果子级和父级属性设置不为空 则开始递归加载数据   
                //    if (!string.IsNullOrEmpty(this.ChildMemberPath) && !string.IsNullOrEmpty(this.ParentMemberPath))   
                //    {   
                //        if (this.ItemSource != null)   
                //        {   
                //            this.LoadTreeViewDataWithRecursion();   
                //        }   
                //    }   
                //}   
                this.SetValue(IsRecursionEnabledProperty, value);
            }
        }

        public static void IsRecursionEnabledCallBack(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            if (sender != null)
            {
                ComboBoxTree comboBoxTree = sender as ComboBoxTree;

                comboBoxTree.IsRecursionEnabled = (bool)args.NewValue;
            }
        }

        #endregion

        #region ChildMemberPath

        public static readonly DependencyProperty ChildMemberPathProperty =
          DependencyProperty.Register("ChildMemberPath", typeof(string), typeof(ComboBoxTree), null);
        /// <summary>   
        /// The Child property item while load by recursion  
        /// </summary>   
        public string ChildMemberPath
        {
            get
            {
                return (string)this.GetValue(ChildMemberPathProperty);
            }
            set
            {
                this.SetValue(ChildMemberPathProperty, value);
            }
        }

        #endregion

        #region ParentMemberPath

        public static readonly DependencyProperty ParentMemberPathProperty =
          DependencyProperty.Register("ParentMemberPath", typeof(string), typeof(ComboBoxTree), null);
        /// <summary>   
        /// This parent property item while load by recursion 
        /// </summary>   
        public string ParentMemberPath
        {
            get
            {
                return (string)this.GetValue(ParentMemberPathProperty);
            }
            set
            {
                this.SetValue(ParentMemberPathProperty, value);
            }
        }

        #endregion

        #region Width

        public static readonly DependencyProperty ComboBoxWidthProperty = DependencyProperty.Register("ComboBoxWidth",
            typeof(double), typeof(ComboBoxTree), null);

        public double ComboBoxWidth
        {
            get
            {
                return (double)this.GetValue(ComboBoxWidthProperty);
            }
            set
            {
                this.SetValue(ComboBoxWidthProperty, value);
                this.cbMain.Width = value;
                double temp = value * 2;
                this.tvList.Width = (temp >= 200) ? 200 : temp;
            }
        }

        #endregion

        #region Height

        public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height",
            typeof(double), typeof(ComboBoxTree), null);

        public double Height
        {
            get
            {
                return (double)this.GetValue(HeightProperty);
            }
            set
            {
                this.SetValue(HeightProperty, value);
                this.cbMain.Height = value;
            }
        }

        #endregion

        #region Style

        public static readonly DependencyProperty StyleProperty = DependencyProperty.Register("Style",
                       typeof(Style), typeof(ComboBoxTree), null);

        public Style Style
        {
            get
            {
                return (Style)this.GetValue(StyleProperty);
            }
            set
            {
                this.SetValue(StyleProperty, value);
                this.cbMain.Style = value;
            }
        }

        #endregion

        #region ItemContainerStyle

        public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle",
                       typeof(Style), typeof(ComboBoxTree), null);

        public Style ItemContainerStyle
        {
            get
            {
                return (Style)this.GetValue(ItemContainerStyleProperty);
            }
            set
            {
                this.SetValue(ItemContainerStyleProperty, value);
                this.cbMain.ItemContainerStyle = value;
            }
        }

        #endregion

        #region HasParent

        public static readonly DependencyProperty HasParentProperty = DependencyProperty.Register("HasParent",
             typeof(bool), typeof(ComboBoxTree), new PropertyMetadata(true));

        public bool HasParent
        {
            get
            {
                return (bool)this.GetValue(HasParentProperty);
            }
            set
            {
                this.SetValue(HasParentProperty, value);
            }
        }

        #endregion

        #endregion

        #region Event Handler

        private void ComboBoxTree_LayoutUpdated(object sender, EventArgs e)
        {
            if (this.IsExpandAll)
            {
                this.tvList.ExpandAll();
            }
        }

        /// <summary>   
        /// TreeView的选择事件   
        /// </summary>   
        /// <param name="sender"></param>   
        /// <param name="e"></param>   
        private void tvList_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            if (tvList.Items.Count > 0)
            {
                this.SelectedItem = tvList.SelectedItem;

                if (string.IsNullOrEmpty(this.DisplayMemberPath) || string.IsNullOrEmpty(this.SelectedValuePath))
                {
                    return;
                }

                Type objType = null;
                PropertyInfo displayMemberInfo = null;
                PropertyInfo selectedValueInfo = null;

                foreach (var item in ItemsSource)
                {
                    //获取实体的类型   
                    objType = item.GetType();
                    //--用反射的方式 获得属性对应的值   
                    displayMemberInfo = objType.GetProperty(this.DisplayMemberPath);
                    selectedValueInfo = objType.GetProperty(this.SelectedValuePath);

                    break;
                }

                TreeViewItem treeviewitem = tvList.SelectedItem as TreeViewItem;

                //--当前要显示的文本 
                string displayText = GetCheckedItemsDisplayString();

                //--当前选择的值  
                if (treeviewitem != null)
                {
                    this.SelectedValue = treeviewitem.Tag.ToString();
                }
                cbItemDisplay.Content = displayText;

                //设置当前选择的项   
                foreach (var item in ItemsSource)
                {
                    string selvalue = selectedValueInfo.GetValue(item, null).ToString();
                    if (!string.IsNullOrEmpty(selvalue) && treeviewitem != null)
                    {
                        if (treeviewitem.Tag.ToString() == selvalue)
                        {
                            this.SelectedItem = item; ;
                        }
                    }
                }
            }
        }

        private void cbMain_DropDownOpened(object sender, EventArgs e)
        {
            //--打开时 隐藏要显示的项   
            cbItemDisplay.Visibility = System.Windows.Visibility.Collapsed;
            if (this.ItemsSource != null)
            {
                cbItemTreeView.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                cbItemTreeView.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        private void cbMain_DropDownClosed(object sender, EventArgs e)
        {
            cbMain.SelectedIndex = -1;

            tvList_SelectedItemChanged(null, null);
            cbMain.SelectedItem = cbItemDisplay;

            cbItemDisplay.Visibility = System.Windows.Visibility.Visible;
            cbItemTreeView.Visibility = System.Windows.Visibility.Collapsed;
        }

        //private void tvList_LostFocus(object sender, RoutedEventArgs e)
        //{
        //    tvList_SelectedItemChanged(null, null);
        //    cbMain.SelectedItem = cbItemDisplay;
        //}

        void c_Unchecked(object sender, RoutedEventArgs e)
        {
            tvList_SelectedItemChanged(null, null);
            cbMain.SelectedItem = cbItemDisplay;
        }

        void c_Checked(object sender, RoutedEventArgs e)
        {
            CheckBox c = sender as CheckBox;
            TreeViewItem item = c.Tag as TreeViewItem;

            if (item.Parent != null)
            {
                TreeViewItem parentItem = item.Parent as TreeViewItem;
                string header = parentItem.Header as string;
                if (!string.IsNullOrEmpty(header))
                {
                    TreeViewItem tempItem;
                    CheckBox tempC;
                    parentItem.Items.ToList().ForEach(i =>
                    {
                        tempItem = i as TreeViewItem;
                        if (tempItem != null && tempItem != item)
                        {
                            tempC = tempItem.Header as CheckBox;
                            if (tempC != null)
                            {
                                tempC.IsChecked = false;
                            }
                        }
                    });
                }
            }

            tvList_SelectedItemChanged(null, null);
            cbMain.SelectedItem = cbItemDisplay;
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Load treeview data by list
        /// </summary>
        private void LoadTreeViewDataWithList()
        {
            this.tvList.Items.Clear();
            foreach (var item in this.ItemsSource)
            {
                Type objType = item.GetType();

                //--用反射的方式 获得属性对应的值   
                PropertyInfo displayMemberInfo = objType.GetProperty(this.DisplayMemberPath);
                PropertyInfo selectedValueInfo = objType.GetProperty(this.SelectedValuePath);

                //--当前要显示的文本   
                string displayText = displayMemberInfo.GetValue(item, null).ToString();
                //--当前选择的值   
                string selectedValue = selectedValueInfo.GetValue(item, null).ToString();

                TreeViewItem myitem = new TreeViewItem();
                CheckBox c = new CheckBox();
                c.DataContext = item;
                Binding binding = new Binding("IsSelected");
                binding.Mode = BindingMode.TwoWay;
                c.SetBinding(CheckBox.IsCheckedProperty, binding);
                c.Content = displayText;
                myitem.Header = c;
                myitem.Tag = selectedValue;

                tvList.Items.Add(myitem);

            }
            tvList_SelectedItemChanged(null, null);
            cbMain.SelectedItem = cbItemDisplay;
        }

        /// <summary>   
        /// 用递归加载数据源   
        /// </summary>   
        private void LoadTreeViewDataWithRecursion()
        {
            tvList.Items.Clear();//先清空所有的数据   

            if (ItemsSource != null)
            {
                //---如果子级和父级属性设置不为空 则开始递归加载数据   
                if (string.IsNullOrEmpty(this.ChildMemberPath) || string.IsNullOrEmpty(this.ParentMemberPath))
                {
                    MessageBox.Show("在【ComboBoxTree】控件中，如果启用了递归方式加载数据，必须设置【ChildMemberPath】和【ParentMemberPath】属性的值。");
                }
                foreach (var item in ItemsSource)
                {
                    Type objType = item.GetType();

                    //--用反射的方式 获得属性对应的值   
                    PropertyInfo displayMemberInfo = objType.GetProperty(this.DisplayMemberPath);
                    PropertyInfo selectedValueInfo = objType.GetProperty(this.SelectedValuePath);
                    PropertyInfo childValueInfo = objType.GetProperty(this.ChildMemberPath);
                    PropertyInfo parentValueInfo = objType.GetProperty(this.ParentMemberPath);

                    //--当前要显示的文本   
                    string displayText = displayMemberInfo.GetValue(item, null).ToString();
                    //--当前选择的值   
                    string selectedValue = selectedValueInfo.GetValue(item, null).ToString();
                    //---子级绑定的值   
                    string childValue = childValueInfo.GetValue(item, null).ToString();
                    //--父级绑定的值   
                    string parentValue = parentValueInfo.GetValue(item, null).ToString();

                    if (string.IsNullOrEmpty(childValue) || string.IsNullOrEmpty(parentValue))
                    {
                        return;
                    }
                    if (parentValue == Guid.Empty.ToString())//--所有的父级的值为【0】的数据默认为添加到根节点。   
                    {
                        TreeViewItem myitem = new TreeViewItem();
                        myitem.Header = displayText;
                        myitem.Tag = selectedValue;
                        this.LoadItems(myitem, ItemsSource);

                        tvList.Items.Add(myitem);
                    }
                }
                tvList_SelectedItemChanged(null, null);
                cbMain.SelectedItem = cbItemDisplay;
            }
        }

        /// <summary>   
        /// 递归加载数据源   
        /// </summary>   
        /// <param name="treeviewitem"></param>   
        /// <param name="mylist"></param>   
        private void LoadItems(TreeViewItem treeviewitem, IEnumerable mylist)
        {
            if (mylist != null)
            {
                foreach (var item in ItemsSource)
                {
                    Type objType = item.GetType();

                    //--用反射的方式 获得属性对应的值   
                    PropertyInfo displayMemberInfo = objType.GetProperty(this.DisplayMemberPath);
                    PropertyInfo selectedValueInfo = objType.GetProperty(this.SelectedValuePath);
                    PropertyInfo childValueInfo = objType.GetProperty(this.ChildMemberPath);
                    PropertyInfo parentValueInfo = objType.GetProperty(this.ParentMemberPath);

                    //--当前要显示的文本   
                    string displayText = displayMemberInfo.GetValue(item, null).ToString();
                    //--当前选择的值   
                    string selectedValue = selectedValueInfo.GetValue(item, null).ToString();
                    //---子级绑定的值   
                    string childValue = childValueInfo.GetValue(item, null).ToString();
                    //--父级绑定的值   
                    string parentValue = parentValueInfo.GetValue(item, null).ToString();

                    if (string.IsNullOrEmpty(childValue) || string.IsNullOrEmpty(parentValue))
                    {
                        return;
                    }
                    if (treeviewitem.Tag.ToString() == parentValue)
                    {
                        TreeViewItem myitem = new TreeViewItem();
                        CheckBox c = new CheckBox();
                        c.DataContext = item;
                        Binding binding = new Binding("IsSelected");
                        binding.Mode = BindingMode.TwoWay;
                        c.SetBinding(CheckBox.IsCheckedProperty, binding);
                        c.Content = displayText;
                        c.Checked += new RoutedEventHandler(c_Checked);
                        c.Unchecked += new RoutedEventHandler(c_Unchecked);
                        c.Tag = myitem;
                        myitem.Header = c;
                        myitem.Tag = selectedValue;

                        treeviewitem.Items.Add(myitem);

                        this.LoadItems(myitem, ItemsSource);
                    }
                }
            }
        }

        private string GetCheckedItemsDisplayString()
        {
            string selectedString = string.Empty;
            int tempLength = 5;
            if (this.Width > 150)
            {
                tempLength = 10;
            }

            if (tvList != null)
            {
                selectedString = GetCheckedString(tvList.Items);
            }

            if (selectedString.Length > tempLength)
            {
                selectedString = selectedString.Substring(0, tempLength) + "...";
            }

            return selectedString;
        }

        private string GetCheckedString(ItemCollection items)
        {
            string returnValue = string.Empty;

            foreach (var i in items)
            {
                TreeViewItem item = i as TreeViewItem;
                CheckBox checkBox = item.Header as CheckBox;

                if (checkBox != null)
                {
                    if (string.IsNullOrEmpty(returnValue))
                    {
                        if (checkBox.IsChecked.HasValue && checkBox.IsChecked.Value)
                        {
                            returnValue = checkBox.Content.ToString();
                        }
                    }
                    else
                    {
                        returnValue = returnValue + "," + checkBox.Content.ToString();
                    }
                }
                else
                {
                    if (string.IsNullOrEmpty(returnValue))
                    {
                        returnValue = GetCheckedString(item.Items);
                    }
                    else
                    {
                        returnValue = returnValue + "," + GetCheckedString(item.Items);
                    }
                }
            }
            return returnValue;
        }

        #endregion
    }


}